package br.com.grimoire.calc;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;

import javax.swing.JButton;

public class CalcControl implements ActionListener {	
	
	private static final int MAXLENGTH = 32;
	
	/* command actions supported */
	public static final String ACT_NUMBER = "number";
	public static final String ACT_POINT = "point";
	public static final String ACT_OPERATION = "operation";
	public static final String ACT_CLEAR = "clear";
	public static final String ACT_BACK = "back";
	public static final String ACT_SIGN = "sign";
	public static final String ACT_MEMORY = "memory";
	
	/* some internal use constants */
	private static final String ZERO = "0";
	private static final String MINUS_SIGN = "-";
	private static final String LOCAL_DEC_SEPAR = String.valueOf(DecimalFormatSymbols.getInstance().getDecimalSeparator());
	private static final String LOCAL_GRP_SEPAR = String.valueOf(DecimalFormatSymbols.getInstance().getGroupingSeparator());
	private static final String JAVA_DEC_SEPAR = ".";
	private static final String JAVA_GRP_SEPAR = ",";
	
	private CalcFrame calc;
	private CalcModel model;
	private StringBuffer buffer;	
	
	public CalcControl(CalcFrame calc) {		
		this.calc = calc;
		model = new CalcModelImpl();
		initBuffer();
	}

	@Override
	public void actionPerformed(ActionEvent event) {
		JButton source = (JButton) event.getSource();
		String value = source.getText();
		String command = source.getActionCommand();
		
		if (command.equals(ACT_NUMBER)) {
			
			if (!buffer.toString().equals(ZERO)
					|| !value.equals(ZERO)) {
				if (buffer.toString().equals(ZERO)
						&& !value.equals(ZERO)) {
					initBuffer();
				}
				buildNumber(value);
			}
		} else if (command.equals(ACT_POINT)) {
			
			if (buffer.indexOf(JAVA_DEC_SEPAR) == -1) {
				if (buffer.length() == 0) {
					initBuffer(ZERO);
				} 
				buildNumber(JAVA_DEC_SEPAR);
			}
		} else if (command.equals(ACT_OPERATION)) {
			
			processOperation(value);
		} else if (command.equals(ACT_SIGN)) {
			
			if (buffer.length() != 0 && !buffer.toString().equals(ZERO)) {
				if (buffer.toString().indexOf(MINUS_SIGN) == -1) {
					buffer.insert(0, MINUS_SIGN);
				} else {
					buffer.deleteCharAt(0);
				}
				setOutput(buffer.toString());
			}
		} else if (command.equals(ACT_CLEAR)) {
			
			if (!buffer.toString().equals(ZERO)) {
				initBuffer(ZERO);
				setOutput(buffer.toString());
			}
		} else if (command.equals(ACT_BACK)) {
			
			if (buffer.length() >= 2) {				
				buffer.deleteCharAt(buffer.length() - 1);
			} else if (buffer.length() < 2) {
				initBuffer(ZERO);
			}
			setOutput(buffer.toString());
		} else if (command.equals(ACT_MEMORY)) {
			
			// not implemented yet
		}  else {
			
		}
	}
	
	private void setOutput(String number) {
		String formattedNumber;
		if (number.indexOf(JAVA_DEC_SEPAR) == -1) {
			formattedNumber = number + LOCAL_DEC_SEPAR;
		} else {
			formattedNumber = number.replace(JAVA_DEC_SEPAR, LOCAL_DEC_SEPAR);
		}
		if (!calc.getDisplayTextField().getText().equals(formattedNumber)) {
			System.out.println(formattedNumber);		
			calc.getDisplayTextField().setText(formattedNumber);
		}		
	}
	
	private void processOperation(String operation) {
		int action;
		BigDecimal result;
		
		if (operation.equals("+")){
			action = CalcModel.ADD;
		} else if (operation.equals("-")) {
			action = CalcModel.SUBTRACT;
		} else if (operation.equals("*")) {
			action = CalcModel.MULTIPLY;
		} else if (operation.equals("/")) {
			action = CalcModel.DIVIDE;
		} else if (operation.equals("=")) {
			action = CalcModel.EQUAL;
		} else if (operation.equals("C")) {
			action = CalcModel.CLEAR;
		} else {
			action = -1;
		}
		
		try {
			if (buffer.length() != 0) {
				result = model.process(new BigDecimal(buffer.toString()), action);
			} else {
				result = model.process(action);
			}	
			setOutput(result.toPlainString());
		} catch (ArithmeticException e) {
			setOutput(e.getMessage()); // TODO correct error messages:
			// 1 / 0 -> Cannot divide by zero.
			// 0 / 0 -> Result of function is undefined.
			// sqrt(-1) -> Invalid input for function.
		}
		
		initBuffer();
	}

	private void buildNumber(String digit) {
		if (buffer.length() < MAXLENGTH) {
			buffer.append(digit);
		}		
		setOutput(buffer.toString());
	}
	
	private void initBuffer() {
		buffer = new StringBuffer(MAXLENGTH);
	}
	
	private void initBuffer(String newBuffer) {
		initBuffer();
		buffer.append(newBuffer);
	}
	
}